﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;

namespace LogExpert
{
	/* Needed info:
	 * - Date/time mask
	 * - index counters 
	 * - counter direction (up/down)
	 * - counter limit
	 * - whether the files are shifted or not
	 * - whether the indexes start with zero (or n/a) on a new date period
	 * 
	 * Format:
	 * *$D(yyyy-MM-dd)$I
	 * *$J(.)
	 * 
	 * *(yyyy-MM-dd)[I]
	 * 
	 */
	/// <summary>
	/// This class is responsible for building file names for multifile.
	/// </summary>
	public class RolloverFilenameBuilder
	{
		//private DateTimeFormatInfo dateFormat;
		private String dateTimeFormat = null;
		private Regex regex;
		//private Regex regexCond;
		private DateTime dateTime;
		private string currentFileName;
		private Group indexGroup;
		private Group dateGroup;
		private Group condGroup;
		private bool hideZeroIndex;
		private string condContent;

		public RolloverFilenameBuilder(string formatString)
		{
			ParseFormatString(formatString);
		}

		public int Index { get; set; }

		private void ParseFormatString(string formatString)
		{
			string fmt = escapeNonvarRegions(formatString);
			int datePos = formatString.IndexOf("$D(");
			if (datePos != -1)
			{
				int endPos = formatString.IndexOf(')', datePos);
				if (endPos != -1)
				{
					this.dateTimeFormat = formatString.Substring(datePos + 3, endPos - datePos - 3);
					this.dateTimeFormat = this.dateTimeFormat.ToUpper();
					this.dateTimeFormat = this.dateTimeFormat.Replace('D', 'd').Replace('Y', 'y');

					string dtf = this.dateTimeFormat;
					dtf = dtf.ToUpper();
					dtf = dtf.Replace("D", "\\d");
					dtf = dtf.Replace("Y", "\\d");
					dtf = dtf.Replace("M", "\\d");
					fmt = fmt.Remove(datePos, 2);   // remove $D
					fmt = fmt.Remove(datePos + 1, this.dateTimeFormat.Length); // replace with regex version of format
					fmt = fmt.Insert(datePos + 1, dtf);
					fmt = fmt.Insert(datePos + 1, "?'date'");  // name the regex group 
				}
			}

			int condPos = fmt.IndexOf("$J(");
			if (condPos != -1)
			{
				int endPos = fmt.IndexOf(')', condPos);
				if (endPos != -1)
				{
					this.condContent = fmt.Substring(condPos + 3, endPos - condPos - 3);
					fmt = fmt.Remove(condPos + 2, endPos - condPos - 1);
				}
			}

			fmt = fmt.Replace("*", ".*");
			this.hideZeroIndex = fmt.Contains("$J");
			fmt = fmt.Replace("$I", "(?'index'[\\d]+)");
			fmt = fmt.Replace("$J", "(?'index'[\\d]*)");

			this.regex = new Regex(fmt);
		}

		private string escapeNonvarRegions(string formatString)
		{
			string fmt = formatString.Replace('*', '\xFFFD');
			StringBuilder result = new StringBuilder();
			int state = 0;
			StringBuilder segment = new StringBuilder();
			for (int i = 0; i < fmt.Length; ++i)
			{
				switch (state)
				{
					case 0:   // looking for $
						if (fmt[i] == '$')
						{
							result.Append(Regex.Escape(segment.ToString()));
							segment = new StringBuilder();
							state = 1;
						}
						segment.Append(fmt[i]);
						break;
					case 1:   // the char behind $
						segment.Append(fmt[i]);
						result.Append(segment.ToString());
						segment = new StringBuilder();
						state = 2;
						break;
					case 2:   // checking if ( or other char
						if (fmt[i] == '(')
						{
							segment.Append(fmt[i]);
							state = 3;
						}
						else
						{
							segment.Append(fmt[i]);
							state = 0;
						}
						break;
					case 3:   // looking for )
						segment.Append(fmt[i]);
						if (fmt[i] == ')')
						{
							result.Append(segment.ToString());
							segment = new StringBuilder();
							state = 0;
						}
						break;
				}
			}
			fmt = result.ToString().Replace('\xFFFD', '*');
			return fmt;
		}

		public void SetFileName(string fileName)
		{
			this.currentFileName = fileName;
			Match match = this.regex.Match(fileName);
			if (match.Success)
			{
				this.dateGroup = match.Groups["date"];
				if (this.dateGroup.Success)
				{
					string date = fileName.Substring(dateGroup.Index, dateGroup.Length);
					if (DateTime.TryParseExact(date, this.dateTimeFormat, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
						out this.dateTime))
					{
					}
				}
				this.indexGroup = match.Groups["index"];
				if (this.indexGroup.Success)
				{
					this.Index = this.indexGroup.Value.Length > 0 ? Int32.Parse(indexGroup.Value) : 0;
				}
				this.condGroup = match.Groups["cond"];
			}
		}

		public void IncrementDate()
		{
			this.dateTime = dateTime.AddDays(1);
		}

		public void DecrementDate()
		{
			this.dateTime = dateTime.AddDays(-1);
		}

		public string BuildFileName()
		{
			string fileName = this.currentFileName;
			if (this.dateGroup != null && this.dateGroup.Success)
			{
				string newDate = dateTime.ToString(this.dateTimeFormat, DateTimeFormatInfo.InvariantInfo);
				fileName = fileName.Remove(this.dateGroup.Index, this.dateGroup.Length);
				fileName = fileName.Insert(this.dateGroup.Index, newDate);
			}
			if (this.indexGroup != null && this.indexGroup.Success)
			{
				fileName = fileName.Remove(this.indexGroup.Index, this.indexGroup.Length);
				string fileNameBak = fileName;
				if (!this.hideZeroIndex || this.Index > 0)
				{
					string format = "D" + this.indexGroup.Length;
					fileName = fileName.Insert(this.indexGroup.Index, this.Index.ToString(format));
					if (this.hideZeroIndex && this.condContent != null)
					{
						fileName = fileName.Insert(this.indexGroup.Index, this.condContent);
					}
				}
			}
			//      this.currentFileName = fileName;
			//      SetFileName(fileName);
			return fileName;
		}

		public bool IsDatePattern
		{
			get
			{
				return this.dateGroup != null && this.dateGroup.Success;
			}
		}

		public bool IsIndexPattern
		{
			get
			{
				return this.indexGroup != null && this.indexGroup.Success;
			}
		}
	}
}